#include "common_util.h"
#include <linux/types.h>
#include <mtd/mtd-abi.h>
#include <mtd/ubi-user.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>

#define MTD_INFO_FILE "/proc/mtd"
#if 0
struct mtd_info_user {
     __u8 type;
    __u32 flags;
    __u32 size;     // Total size of the MTD
 
    __u32 erasesize;
    __u32 writesize;
    __u32 oobsize;// Amount of OOB data per block (e.g. 16)
    /*
 The below two fields are obsolete and broken, do not use them * (TODO: remove at some point) */
 
    __u32 ecctype;
    __u32 eccsize;
};

#define MEMGETINFO        _IOR('M', 1, struct mtd_info_user) 
#define MEMERASE        _IOW('M', 2, struct erase_info_user) 
#define MEMWRITEOOB        _IOWR('M', 3, struct mtd_oob_buf) 
#define MEMREADOOB        _IOWR('M', 4, struct mtd_oob_buf) 
#define MEMLOCK            _IOW('M', 5, struct erase_info_user) 
#define MEMUNLOCK        _IOW('M', 6, struct erase_info_user) 
#define MEMGETREGIONCOUNT    _IOR('M', 7, int) 
#define MEMGETREGIONINFO    _IOWR('M', 8, struct region_info_user) 
#define MEMSETOOBSEL        _IOW('M', 9, struct nand_oobinfo) 
#define MEMGETOOBSEL        _IOR('M', 10, struct nand_oobinfo) 
#define MEMGETBADBLOCK        _IOW('M', 11, __kernel_loff_t) 
#define MEMSETBADBLOCK        _IOW('M', 12, __kernel_loff_t) 
#define OTPSELECT        _IOR('M', 13, int) 
#define OTPGETREGIONCOUNT    _IOW('M', 14, int) 
#define OTPGETREGIONINFO    _IOW('M', 15, struct otp_info) 
#define OTPLOCK            _IOR('M', 16, struct otp_info) 
#define ECCGETLAYOUT        _IOR('M', 17, struct nand_ecclayout_user) 
#define ECCGETSTATS        _IOR('M', 18, struct mtd_ecc_stats) 
#define MTDFILEMODE        _IO('M', 19) 
#define MEMERASE64        _IOW('M', 20, struct erase_info_user64) 
#define MEMWRITEOOB64        _IOWR('M', 21, struct mtd_oob_buf64) 
#define MEMREADOOB64        _IOWR('M', 22, struct mtd_oob_buf64) 
#define MEMISLOCKED        _IOR('M', 23, struct erase_info_user)
#endif

int common_util_get_block_size(char *dev_node)
{
   struct mtd_info_user mtd;
   int fd = -1;
   fd = open (dev_node, O_SYNC | O_RDWR);
   if(fd >= 0)
   {
      ioctl(fd, MEMGETINFO, &mtd) ;
      return mtd.erasesize;
   }
   else
   {
     printf("open %s file , %s\n", dev_node, strerror(errno));
   }
   return -1;
}



int common_util_is_bad_block(uint64_t address, uint32_t erase_block_size)
{



}


int common_util_erase_partition(char *dev_node)
{
   int isNAND, bbtest = 1;
   int fd = -1;
   int quiet = 0; //bug print enable
   struct mtd_info_user mtd;
   erase_info_t erase;
   fd = open (dev_node, O_SYNC | O_RDWR);
   if(fd >= 0)
   {
      ioctl(fd, MEMGETINFO, &mtd) ;
      struct mtd_info_user *DevInfo = &mtd;      
      erase.length = DevInfo->erasesize;

      isNAND = (DevInfo->type == MTD_NANDFLASH) ? 1 : 0;
      printf("mtd size %d erase size %d\n", DevInfo->size, DevInfo->erasesize);
      for (erase.start = 0; erase.start <  DevInfo->size; erase.start += DevInfo->erasesize) 
      {
            int ret = 0;
            if (bbtest) 
            {
                loff_t offset = erase.start;
                ret = ioctl(fd, MEMGETBADBLOCK, &offset); //判断是不是坏块
                if (ret != 0) 
                {
                    if (!quiet)
                        printf ("\nSkipping bad block at 0x%08x\n", erase.start);
                    continue;//发现是坏块，应该跳过
                } 
                #if 0  //if not need this judge,disable it
                            else if (ret < 0) 
                            {
                                if (errno == EOPNOTSUPP) 
                                {
                                    bbtest = 0;
                                    if (isNAND) 
                                    {
                                        fprintf(stderr, "%s: Bad block check not available\n", DevInfo->dir);
                                        return 1;
                                    }
                                } 
                                else 
                                {
                                    fprintf(stderr, "\n%s: MTD get bad block failed: %s\n", DevInfo->dir, strerror(errno));
                                    return 1;
                                }
                            }
                #endif
             }
                
            if (!quiet)
            {
                fprintf(stderr, "\rErasing %d Kibyte @ %x -- %2llu %% complete. ret %d", \
                    (DevInfo->erasesize) / 1024, erase.start,
                    (unsigned long long) erase.start * 100 / (DevInfo->size), ret);
            }
            if (ioctl(fd, MEMERASE, &erase) != 0) //执行擦除操作
            {
                fprintf(stderr, "\n%s: MTD Erase failure: %s\n", dev_node,strerror(errno));
                continue;
            }
         }


   }
  else
   {
     printf("open %s file , %s\n", dev_node, strerror(errno));
   }
   close(fd);
   return 0;

}

static int get_file_size( char *file)
{
   struct stat s_buf;
   if(file == NULL)
      return -1;
   if(stat(file, &s_buf) == 0)
   {
      return s_buf.st_size;
   }
   return -1;

}

int common_util_write_file_to_partition(char *dev_node, char *image_file)
{
   int image_file_size = get_file_size(image_file);
   printf("\nimage file size %d\n", image_file_size);
   int fd = -1;
   int fd_image = -1;
   char *write_buf = NULL;
   struct mtd_info_user mtd;
   erase_info_t erase;
   fd_image = open (image_file, O_SYNC | O_RDWR);  
   if(fd_image < 0)
      return -1;
   fd = open (dev_node,  O_RDWR);  
   if(fd >= 0)
   {
       ioctl(fd, MEMGETINFO, &mtd);
     //  ioctl(fd, UBI_IOCVOLUP, &image_file_size);
       loff_t oft = lseek(fd, 0 , SEEK_CUR);
       erase.length = mtd.erasesize;
       int write_num = 0;
      // printf("partition size %d writesize:%d offset: %lld \n", mtd.size, mtd.writesize, oft);
       int writesize = mtd.erasesize;
       write_buf = malloc(writesize);
       if(write_buf == NULL)
       {
         printf("write_buf mem malloc failed\n");
         return -1;
       }
       
       while(write_num < mtd.size /*&& write_num < image_file_size*/)
       {
            memset(write_buf, 0, writesize);
            ssize_t  len = read(fd_image, write_buf, writesize);
            if(len == 0)
            {
                memset(write_buf, 0xff, writesize);
                write_num += writesize;
            }
               
            int ret = 0;
            loff_t block_num = 0;
            NEXT_BLOCK:
                oft = lseek(fd, 0 , SEEK_CUR);
                block_num = (oft / mtd.erasesize) * mtd.erasesize;
                erase.start = block_num; 
                ret = ioctl(fd, MEMGETBADBLOCK, &block_num); //判断是不是坏块
                if(ret < 0)
                   printf("%d %s\n", ret, strerror(errno));
                if (ret > 0)  /*is bad block*/
                {
                    printf("is a bad block\n");
                   // oft += mtd.erasesize;
                    write_num += writesize;
                    lseek(fd, mtd.erasesize , SEEK_CUR);
                    goto NEXT_BLOCK;
                }
                else
                {
                   if (ioctl(fd, MEMERASE, &erase) != 0) //执行擦除操作
                    {
                        fprintf(stderr, "\n%s: MTD Erase failure: %s\n", dev_node,strerror(errno));
                        goto FAILED;
                    }
                    int  write_ret = write(fd, write_buf, len);
                   // printf("\nblock_num: %lld oft: %lld len:%d write_ret:%d\n", block_num, oft, len, write_ret);
                    if(write_ret != len)
                    {
                        printf("write file, exit\n");
                        goto FAILED;
                    }
                }
                
                write_num += len;
                fprintf(stderr,"\rwrite num : %d(kb) complete: %f", write_num/1024 , (float)write_num /  (float)image_file_size);
       }


   }
    free(write_buf);
    write_buf = NULL;
    fsync(fd);
    close(fd);
    
    close(fd_image);
    return 0;
   FAILED:
    free(write_buf);
    write_buf = NULL;
    close(fd);
    close(fd_image);
    return -1;
}